﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;

using vJine.Core.IoC;
using vJine.Core.ORM;

namespace vJine.Core.IO.Xml {
    public partial class XmlHelper {

        internal static T Parse<T>(XmlNode nodeXml)
            where T : class, new() {

            T objXml = new T(); 
            XmlHelper.Parse<T>(nodeXml, objXml);

            return objXml;
        }

        static MethodInfo parseObject =
            new Exec<XmlNode, XmlHelper>(Parse<XmlHelper>).Method.GetGenericMethodDefinition();
        static void Parse<T>(XmlNode xmlNode, T xmlObject)
            where T : class, new() {

            Class<T>.Property[] P = Class<T>.GetMap();
            for (int i = 0, len = P.Length; i < len; i++) {
                Class<T>.Property p_i = P[i]; Type p_type = p_i.pType;
                if (p_i.IsXmlIgnore) {
                    continue;
                }

                if(p_i.IsObject) {
                    string v = getNodeValue<T>(p_i, xmlNode);
                    if(v == null) {
                        continue;
                    }
                    p_i.Set(xmlObject, v);
                } else if (p_i.IsPrimitive || p_type == Reflect.@type) {
                    string v = getNodeValue<T>(p_i, xmlNode);
                    if(v == null){
                        continue;
                    }
                    p_i.Set(xmlObject, Class.Parse(v, p_type));

                } else if (p_type == Reflect.byteArray) {
                    XmlElement xmlObj = xmlNode[p_i.Name];

                    string v = xmlObj == null ? null : xmlObj.InnerText;
                    if (string.IsNullOrEmpty(v)) {
                        p_i.Set(xmlObj, null); continue;
                    }
                    p_i.Set(xmlObject, Convert.FromBase64String(v));

                } else {
                    XmlNode objNode = xmlNode[p_i.Name];
                    if (objNode == null || objNode.NodeType == XmlNodeType.Comment) {
                        p_i.Set(xmlObject, null); continue;
                    }

                    object objObj = p_i.Get(xmlObject);
                    objObj = objObj ?? Class.Create(p_type);

                    parseObject.MakeGenericMethod(p_type)
                        .Invoke(null, new object[] { objNode, objObj });

                    p_i.Set(xmlObject, objObj);
                }
            }

            if(Reflect.IsList(xmlObject)) {
                parseList
                    .MakeGenericMethod(XmlHelper.GetListArg(typeof(T)))
                    .Invoke(null, new object[] { xmlNode.ChildNodes, xmlObject });
            } else if(Reflect.IsDictionary(xmlObject)) {
                parseDict
                    .MakeGenericMethod(XmlHelper.GetDictArg(typeof(T)))
                    .Invoke(null, new object[] { xmlNode.ChildNodes, xmlObject });
            }
        }

        static MethodInfo parseList =
            new Exec<XmlNodeList, IList<XmlHelper>>(Parse<XmlHelper>).Method.GetGenericMethodDefinition();
        static void Parse<T>(XmlNodeList xmlList, IList<T> xmlObject) where T : class, new() {

            Property<T, bool>[] ActiveFlags = Class<T>.ActiveFlags;
            int len_flags = ActiveFlags.Length;

            string nodeName = Class<T>.Name; //TODO:自定义节点名称

            for (int i = 0, len = xmlList.Count; i < len; i++) {
                XmlNode xml_i = xmlList[i];
                if (xml_i.NodeType == XmlNodeType.Comment) {
                    continue;
                }
                if (xml_i.Name != nodeName) {
                    continue;
                }

                T xmlObj = new T();

                Parse<T>(xml_i, xmlObj);

                bool Ignored = false;
                for (int j = 0; j < len_flags; j++) {
                    if (!ActiveFlags[j].Get(xmlObj)) {
                        Ignored = true; break;
                    }
                }

                if (!Ignored) {
                    xmlObject.Add(xmlObj);
                }
            }
        }

        static MethodInfo parseDict =
            new Exec<XmlNodeList, IDictionary<string, XmlHelper>>(Parse<string, XmlHelper>).Method.GetGenericMethodDefinition();
        static void Parse<K, V>(XmlNodeList xmlDict, IDictionary<K, V> xmlObject)
            where V : class, new() {

            Property<V, bool>[] ActiveFlags = Class<V>.ActiveFlags;
            int len_flags = ActiveFlags.Length;

            Class<V>.Property[] Keys = Class<V>.Keys;
            if (Keys.Length != 1) {
                throw new CoreException("No Keys Available For Type:[{0}]", Class<V>.FullName);
            }
            Class<V>.Property p_key = Keys[0];

            for (int i = 0, len = xmlDict.Count; i < len; i++) {
                XmlNode xml_i = xmlDict[i];
                if (xml_i.NodeType == XmlNodeType.Comment) {
                    continue;
                }

                K key = 
                    Class.Parse<K>(getNodeValue<V>(p_key, xml_i));

                V v_dict = new V();
                Parse<V>(xml_i, v_dict);

                bool Ignored = false;
                for (int j = 0; j < len_flags; j++) {
                    if (!ActiveFlags[j].Get(v_dict)) {
                        Ignored = true; break;
                    }
                }

                if (!Ignored) {
                    if (xmlObject.ContainsKey(key)) {
                        xmlObject[key] = v_dict;
                    } else {
                        xmlObject.Add(key, v_dict);
                    }
                }
            }
        }

        static string getNodeValue<T>(Class<T>.Property p_i, XmlNode xmlNode) {
            string v = null;
            if (p_i.IsXmlAttribute || p_i.IsObject) {
                XmlAttribute xmlObj = xmlNode.Attributes[p_i.Name];
                v = xmlObj == null ? null : xmlObj.Value;
            } else if (p_i.IsXmlElement || p_i.IsObject) {
                XmlElement xmlObj = xmlNode[p_i.Name];
                v = xmlObj == null ? null : xmlObj.InnerText;
            } else if (p_i.IsXmlText || p_i.IsObject) {
                v = xmlNode.InnerText;
            } else {
                throw new CoreException("XML Node Error:[{1}]", p_i.Name);
            }

            return v;
        }

        static Type GetListArg(Type Tobj) {
            Type[] Targs = Reflect.GetGenericArgs(Tobj);
            if (Targs == null || Targs.Length == 0) {
                throw new CoreException(
                    MethodBase.GetCurrentMethod(), "[{0}] Is Not Generic Type", Tobj.FullName);
            } else if (Targs.Length != 1) {
                throw new CoreException(
                    MethodBase.GetCurrentMethod(), "[{0}] Generic Type Has Too Much Args", Tobj.FullName);
            }

            return Targs[0];
        }

        static Type[] GetDictArg(Type Tobj) {
            Type[] Targs = Reflect.GetGenericArgs(Tobj);
            if (Targs == null || Targs.Length == 0) {
                throw new CoreException(
                    MethodBase.GetCurrentMethod(), "[{0}] Is Not Generic Type", Tobj.FullName);
            } else if (Targs.Length != 2) {
                throw new CoreException(
                    MethodBase.GetCurrentMethod(), "[{0}] Generic Type Has Too Much Args", Tobj.FullName);
            }

            return Targs;
        }
    }
}
